'''
Created on Sep 28, 2010

@author: morgan
'''

import os.path
from __xml__ import xml
from tempfile import NamedTemporaryFile
from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED
from urllib import url2pathname
from doc import kml

__all__ = ['kmz', 'ZIP_STORED', 'ZIP_DEFLATED']

class kmz(ZipFile):
    """ 
    The kml.kmz class is a specialized zipfile.ZipFile subclass that can 
    transparently add all local files referenced in href and targetHref
    objects/elements. The files must exist on the local filesystem in the 
    current directory and be referenced by relative paths. (This behavior
    is likely to change in the near future)
    
    Note that since kmz must walk the KML document tree, it must be compliant 
    XML and KML.  
    """
    def __init__(self, kml, filename, mode=None, 
                 compression=ZIP_STORED, allowZip64=False ):
        if not mode:
            if os.path.exists(filename):
                mode='r'
            else:
                mode='w'
        ZipFile.__init__(self, filename, mode, compression, allowZip64)
        self.kml = kml
        self._localfiles = []
    
    @property 
    def kml(self):
        """
        A kml.kml instance that contains the KMZ's doc.kml.
        """
        return self._kml
    @kml.setter
    def kml(self, value):
        if type(value) == kml:
            self._kml = value
        else:
            raise ValueError("Expecting kml to be a kml.kml instance")
    
    @property
    def localFiles(self):
        """ 
        A read-only property containing a list of all local files snarfed
        from the KMZ's doc.kml file.
        """
        self.__walk__(self.kml._doc, 'href')
        # XXX TODO XXX: Proper support for <sourceHref> and <targetHref>
        self.__walk__(self.kml._doc, 'targetHref')
        return self._localfiles

    def __walk__(self, parent, tagName):
        """ 
        This method walks main KML document's (doc.kml) tree, searching for a 
        text element named I{tagName} 
        """
        for node in parent.childNodes:
            if node.nodeType == xml.Node.ELEMENT_NODE:
                for child in node.childNodes:
                    if str(child.nodeName) == tagName:
                        url = child.childNodes[0].data
                        if os.path.exists(url2pathname(url)):
                            self._localfiles.append(url)
                self.__walk__(node, tagName)
    
    def write(self, filename="", *args):
        """
        This method overrides zipfile.ZipFile's write method.  If no arguments
        are specified, it will add file specified by kml.filename to the
        kmz file, along with all files returned by the kmz.localFiles property.
        
        You can add any arbitrary file to the KMZ file by specifying the usual
        arguments to zipfile.ZipFile's write method. 
        """
        if not filename:
            for filename in self.localFiles:
                ZipFile.write(self, filename)
            ZipFile.write(self, self.kml.filename)
        else:
            ZipFile.write(self, filename, *args)
            
    def read(self, filename="", pwd=None):
        """
        This method overrides zipfile.ZipFile's read method.  If no arguments
        are specified, it will extract the main KML file (doc.kml) from the 
        KMZ file to a temporary file and subsequently return a kml.kml instance
        containing the parsed KML document.  The temporary file is automatically
        deleted.
        
        You can read any arbitrary file from the KMZ file by specifying the
        normal arguments to zipfile.ZipFile's read method.
        """
        if not filename:
            docfile = ZipFile.read(self, "doc.kml", pwd)
            tmpfile = NamedTemporaryFile('w', dir='/tmp', delete=False)
            tmpfile.write(docfile)
            tmpfile.close()
            doc = kml(tmpfile.name)
            os.remove(tmpfile.name)
            return doc
        else:
            return ZipFile.read(self, filename, pwd)
        